package org.lex.swt.app;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.jface.action.IAction;

public class ActionMap {
	/** Handles the mapping between Action name and Action. */
	private transient Map<Object, IAction> map = new HashMap<Object, IAction>();
	/** Parent that handles any bindings we don't contain. */
	private ActionMap parent;

	/**
	 * Creates an <code>ActionMap</code> with no parent and no mappings.
	 */
	public ActionMap() {
	}

	/**
	 * Sets this <code>ActionMap</code>'s parent.
	 * 
	 * @param map
	 *            the <code>ActionMap</code> that is the parent of this one
	 */
	public void setParent(ActionMap map) {
		this.parent = map;
	}

	/**
	 * Returns this <code>ActionMap</code>'s parent.
	 * 
	 * @return the <code>ActionMap</code> that is the parent of this one, or
	 *         null if this <code>ActionMap</code> has no parent
	 */
	public ActionMap getParent() {
		return parent;
	}

	/**
	 * Adds a binding for <code>key</code> to <code>action</code>. If
	 * <code>action</code> is null, this removes the current binding for
	 * <code>key</code>.
	 * <p>
	 * In most instances, <code>key</code> will be
	 * <code>action.getValue(NAME)</code>.
	 */
	public void put(Object key, IAction action) {
		if (key == null) {
			return;
		}
		if (action == null) {
			remove(key);
		} else {
			map.put(key, action);
		}
	}

	/**
	 * Returns the binding for <code>key</code>, messaging the parent
	 * <code>ActionMap</code> if the binding is not locally defined.
	 */
	public IAction get(Object key) {
		IAction value = map.get(key);

		if (value == null) {
			ActionMap parent = getParent();
			if (parent != null) {
				return parent.get(key);
			}
		}
		return value;
	}

	/**
	 * Removes the binding for <code>key</code> from this <code>ActionMap</code>
	 * .
	 */
	public void remove(Object key) {
		map.remove(key);
	}

	/**
	 * Removes all the mappings from this <code>ActionMap</code>.
	 */
	public void clear() {
		map.clear();
	}

	/**
	 * Returns the <code>Action</code> names that are bound in this
	 * <code>ActionMap</code>.
	 */
	public Collection<Object> keys() {
		return map.keySet();
	}

	/**
	 * Returns the number of <code>KeyStroke</code> bindings.
	 */
	public int size() {
		return map.size();
	}

	/**
	 * Returns an array of the keys defined in this <code>ActionMap</code> and
	 * its parent. This method differs from <code>keys()</code> in that this
	 * method includes the keys defined in the parent.
	 */
	public Collection<Object> allKeys() {
		ActionMap parent = getParent();
		if (parent == null) {
			return keys();
		}

		if (map.isEmpty()) {
			if (parent != null) {
				return parent.allKeys();
			}
			return keys();
		}
		Collection<Object> keys = keys();
		Collection<Object> pKeys = parent.allKeys();

		if (pKeys.isEmpty()) {
			return keys;
		}
		if (keys.isEmpty()) {
			return pKeys;
		}

		Set<Object> all = new HashSet<Object>();
		all.addAll(keys);
		all.addAll(pKeys);
		return all;
	}
}
